/*  This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 2.1 of the License, or (at
    your option) any later version.
    For more details, see the GNU Lesser General Public License (www.fsf.org
    or the COPYING file somewhere in the package)
 */

#ifndef OSGI_LIFECYCLE_HH
#define OSGI_LIFECYCLE_HH

#include "OSGi.hh"

/*! @file OSGi/LifeCycle.hh
    @brief Class OSGi::LifeCycle header.
    @author @ref Guillaume_Terrissol
    @date 8th February 2010 - 26th May 2013
    @note This file is distributed under the LGPL license.
    Refer to the file COPYING (or http://www.fsf.org) for more information.
 */

#include <memory>
#include <string>

#include "PImpl.hh"

namespace OSGi
{
//------------------------------------------------------------------------------
//                                  Life Cycle
//------------------------------------------------------------------------------

    /*! @brief State machine : bundle life cycle.
        @version 0.5

        The class is a state machine, driving the bundle life cyle.@n
        This state machine may be customized thanks to the BundleFactory (see
        BundleFactory::build(LifeCycle::Ptr pLifeCycle) const).
    */
    class OSGI_EXPORT LifeCycle
    {
    public:
        //! @name Pointer types
        //@{
        using Ptr        = std::shared_ptr<LifeCycle>;                          //!< Shared pointer to bundle life cycle.
        using ContextPtr = std::shared_ptr<Context>;                            //!< Pointer to context.
        //@}
        //! Recursion type.
        enum class ERecurse
        {
            eNo,                                                               //!< None.
            eThisFirst,                                                        //!< "This" bundle first.
            eThisLast                                                          //!< "This" bundle last.
        };
        /*! @brief State change.
            @version 0.5
            
            Every state change is done thanks to an instance of this class.@n
            A bundle method is called to try the change, which returns @b on
            success.
            @sa declareChange()
         */
        class Change
        {
        public:
            //! @name Pointer type
            //@{
            using Ptr = std::shared_ptr<Change>;                                //!< Pointer to change.
            //@}
            //! @name Constructors & destructor
            //@{
                        Change();                                               //!< Default constructor.
                        Change(std::string pToState, ERecurse pRecursion);      //!< Nominal constructor.
    virtual             ~Change();                                              //!< Destructor.
            //@}
            //! @name
            //@{
            bool        operator()(Bundle* pBundle, ContextPtr pContext) const; //!< Function operator.
            std::string toState() const;                                        //!< Target state.
            ERecurse    doesRecurse() const;                                    //!< Recursion type.
            //@}
        private:

    virtual bool        call(Bundle* pBundle, ContextPtr pContext) const = 0;   //!< Change [attempt].
            std::string mToState;                                               //!< Target state.
            ERecurse    mRecursion;                                             //!< Recursion type.
        };
        //! @name Constructor & destructor.
        //@{
                    LifeCycle();                                                //!< Default constructor.
                    ~LifeCycle();                                               //!< Destructor.
        //@}
        //! @name Declarations
        //@{
        void        declareInitialState(std::string pName);                     //!< Declaration of the initial state.
        void        declareState(std::string pName);                            //!< Declaration of a state.
        template<class TBndl>
        void        declareChange(std::string   pName,
                                  std::string   pFrom,
                                  std::string   pTo,
                                  bool (TBndl::*pFunc)(ContextPtr));            //!< Declaration of a change.
        template<class TBndl>
        void        declareChange(std::string   pName,
                                  std::string   pFrom,
                                  std::string   pTo,
                                  bool (TBndl::*pFunc)(ContextPtr),
                                  ERecurse      pRecursion);                    //!< Declaration of a change with recursion.
        //@}
        //! @name Information
        //@{
        std::string initialState() const;                                       //!< Initial state.
        Change::Ptr change(std::string pName, std::string pFrom) const;         //!< Change to transition.
        //@}

    private:

        void        declareChange(std::string pName,
                                  std::string pFrom,
                                  std::string pTo,
                                  Change::Ptr pChange);                         //!< Change declaration.

        template<class TBndl>
        class GenericChange;

        OSGI_PIMPL()
    };


//------------------------------------------------------------------------------
//                               String Constants
//------------------------------------------------------------------------------

    /*! @defgroup OSGi_Bundle_Changes_Group Changes
        @details These are the actions to apply on a Bundle to make it change
        its current @ref OSGi_Bundle_States_Group "state". @n
        See @ref OSGi_LifeCycle_Graph "LifeCycle" for the default
        bundle life cycle.
        @{
     */
    extern const std::string    kResolve;       //!< Resolve bundle.
    extern const std::string    kStart;         //!< Start bundle.
    extern const std::string    kStop;          //!< Stop bundle.
    extern const std::string    kUninstall;     //!< Uninstall bundle.
    //@}
    /*! @defgroup OSGi_Bundle_States_Group States
        @details These are the states a standard Bundle may be in. @n
        See @ref OSGi_LifeCycle_Graph "LifeCycle" for the default
        bundle life cycle.
        @{
     */
    extern const std::string    kInstalled;     //!< Bundle is installed.
    extern const std::string    kUninstalled;   //!< Bundle is uninstalled.
    extern const std::string    kResolved;      //!< Bundle is resolved.
    extern const std::string    kStarting;      //!< Bundle is starting.
    extern const std::string    kActive;        //!< Bundle is active.
    extern const std::string    kStopping;      //!< Bundle is stopping.
    //@}
}

#include "LifeCycle.inl"

#endif  // OSGI_LIFECYCLE_HH
